using System;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;

namespace CSW.Framework.UI.UserControls
{
    /// <summary>
    /// ListView capable of drag and drop reordering.
    /// </summary>
    public class DragAndDropListView : ListView
    {
        private const string REORDER = "Reorder";
        private bool m_AllowRowReorder = true;

        /// <summary>
        /// Occurs after the row order has changed.
        /// </summary>
        public event EventHandler AfterReorder;

        /// <summary>
        /// Initializes a new instance of the <see cref="DragAndDropListView"/> class.
        /// </summary>
        public DragAndDropListView()
        {
            AllowRowReorder = true;
        }

        /// <summary>
        /// Gets or sets a value indicating whether to allow row reorder.
        /// </summary>
        /// <value><c>true</c> if row reorder is allowed; otherwise, <c>false</c>.</value>
        public bool AllowRowReorder
        {
            get
            {
                return m_AllowRowReorder;
            }
            set
            {
                m_AllowRowReorder = value;
                base.AllowDrop = value;
            }
        }

        /// <summary>
        /// Gets or sets the sort order for items in the control.
        /// </summary>
        /// <value></value>
        /// <returns>One of the <see cref="T:System.Windows.Forms.SortOrder"/> values. The default is <see cref="F:System.Windows.Forms.SortOrder.None"/>.</returns>
        /// <exception cref="T:System.ComponentModel.InvalidEnumArgumentException">The value specified is not one of the <see cref="T:System.Windows.Forms.SortOrder"/> values. </exception>
        /// <PermissionSet>
        /// 	<IPermission class="System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// 	<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// 	<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/>
        /// 	<IPermission class="System.Diagnostics.PerformanceCounterPermission, System, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Unrestricted="true"/>
        /// </PermissionSet>
        public new SortOrder Sorting
        {
            get
            {
                return SortOrder.None;
            }
            // ReSharper disable ValueParameterNotUsed
            set
            {
                base.Sorting = SortOrder.None;
            }
            // ReSharper restore ValueParameterNotUsed
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.DragDrop"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DragEventArgs"/> that contains the event data.</param>
        protected override void OnDragDrop(DragEventArgs e)
        {
            base.OnDragDrop(e);
            if (!AllowRowReorder)
            {
                return;
            }
            if (SelectedItems.Count == 0)
            {
                return;
            }
            Point cp = PointToClient(new Point(e.X, e.Y));
            ListViewItem dragToItem = GetItemAt(cp.X, cp.Y);
            if (dragToItem == null)
            {
                return;
            }
            int dropIndex = dragToItem.Index;
            if (dropIndex > SelectedItems[0].Index)
            {
                dropIndex++;
            }
            ArrayList insertItems =
                new ArrayList(SelectedItems.Count);
            foreach (ListViewItem item in SelectedItems)
            {
                insertItems.Add(item.Clone());
            }

            int i;
            for (i = insertItems.Count - 1; i >= 0; i--)
            {
                ListViewItem insertItem = (ListViewItem)insertItems[i];
                Items.Insert(dropIndex, insertItem);
            }
            foreach (ListViewItem removeItem in SelectedItems)
            {
                Items.Remove(removeItem);
            }
            for (i = insertItems.Count - 1; i >= 0; i--)
            {
                ListViewItem insertItem = (ListViewItem)insertItems[i];
                insertItem.Selected = true;
            }

            EventHandler afterRecorder = AfterReorder;
            if (afterRecorder != null)
            {
                afterRecorder(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.DragOver"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DragEventArgs"/> that contains the event data.</param>
        protected override void OnDragOver(DragEventArgs e)
        {
            if (!AllowRowReorder)
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            if (!e.Data.GetDataPresent(DataFormats.Text))
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            Point cp = PointToClient(new Point(e.X, e.Y));
            ListViewItem hoverItem = GetItemAt(cp.X, cp.Y);
            if (hoverItem == null)
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            foreach (ListViewItem moveItem in SelectedItems)
            {
                if (moveItem.Index == hoverItem.Index)
                {
                    e.Effect = DragDropEffects.None;
                    hoverItem.EnsureVisible();
                    return;
                }
            }
            base.OnDragOver(e);
            string text = (string)e.Data.GetData(REORDER.GetType());
            if (text.CompareTo(REORDER) == 0)
            {
                e.Effect = DragDropEffects.Move;
                hoverItem.EnsureVisible();
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.Control.DragEnter"/> event.
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.DragEventArgs"/> that contains the event data.</param>
        protected override void OnDragEnter(DragEventArgs e)
        {
            base.OnDragEnter(e);
            if (!AllowRowReorder)
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            if (!e.Data.GetDataPresent(DataFormats.Text))
            {
                e.Effect = DragDropEffects.None;
                return;
            }
            base.OnDragEnter(e);
            string text = (string)e.Data.GetData(REORDER.GetType());
            if (text.CompareTo(REORDER) == 0)
            {
                e.Effect = DragDropEffects.Move;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        /// <summary>
        /// Raises the <see cref="E:System.Windows.Forms.ListView.ItemDrag"/> event.
        /// </summary>
        /// <param name="e">An <see cref="T:System.Windows.Forms.ItemDragEventArgs"/> that contains the event data.</param>
        protected override void OnItemDrag(ItemDragEventArgs e)
        {
            base.OnItemDrag(e);
            if (!AllowRowReorder)
            {
                return;
            }
            DoDragDrop(REORDER, DragDropEffects.Move);
        }
    }
}